﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ControleRepresentante.Logic.ImpostoHandlers;
using ControleRepresentante.Logic.Models;
using ControleRepresentante.Logic.Repositories.SearchExtensions;
using ControleRepresentante.Logic.Statistics;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.SqlCommand;
using Attribute = ControleRepresentante.Logic.Models.Attribute;

namespace ControleRepresentante.Logic.Repositories
{
    public interface IPedidoRepository : IAttributeHolderRepository<Pedido, PedidoStatistics>
    {
        Dictionary<string, double> GetVendasForVendedor(Guid vendedorId);
        void AddPost(Post post);
        IEnumerable<Pedido> GetModels(int? numero, IEnumerable<Guid> vendedorIds, string cliente, IEnumerable<Guid> fabricaIds, IEnumerable<Guid> transportadoraIds, DateTime? de, DateTime? ate, double? totalMin, double? totalMax, string observacao, bool? faturado, bool? cancelado, int? numeroProdutos, int? numeroPecas, string descricaoProduto, DateTime? criadoDe, DateTime? criadoAte, DateTime? modificadoDe, DateTime? modificadoAte, Dictionary<Attribute, string> attributeMap, ISearchExtension[] searchExtensions, out PedidoStatistics statistics, int page);
        string[] FilterSynchronizedPedidos(string[] pedidos);
        void AddModels(Pedido[] pedidos);
        IEnumerable<Pedido> GetModels(Guid? vendedorId, Guid? clienteId, Guid? fabricaId, Guid? transportadoraId, int count);
        IEnumerable<CompareYear> GetCompare(CompareTotal compareTotal, IEnumerable<Guid> fabricaIds, IEnumerable<Guid> vendedorIds, string cliente, IEnumerable<Guid> transportadoraIds, bool allJolitex);
    }

    public enum CompareTotal
    {
        TotalMercadoria,
        TotalLiquido,
        TotalPecas,
        Count
    }

    public class PedidoRepository : AttributeHolderRepository<Pedido, PedidoStatistics, PedidoError>, IPedidoRepository
    {
        private readonly IImpostoProvider m_ImpostoProvider;
        private int? m_PageSize;

        public PedidoRepository(IRepositoryConfiguration repositoryConfiguration, ISession session, IImpostoProvider impostoProvider)
            : base(repositoryConfiguration, session)
        {
            m_ImpostoProvider = impostoProvider;
        }

        protected override IQueryOver<Pedido, Pedido> GetModelsQueryOver(IQueryOver<Pedido, Pedido> queryOver)
        {
            return AddFiltersToQueryOver(queryOver)
                .OrderBy(x => x.Data).Desc
                .OrderBy(x => x.Numero).Desc;
        }

        protected override IQueryOver<Pedido, Pedido> DoGetModelByIdQueryOver(IQueryOver<Pedido, Pedido> queryOver, Guid id)
        {
            return AddFiltersToQueryOver(queryOver);
        }

        protected override IEnumerable<Expression<Func<Pedido, bool>>> DoGetUniquenessExpressions(Pedido model)
        {
            yield return x => x.Id == model.Id;
        }

        protected override PedidoError RepresentanteMissmatchError
        {
            get { return PedidoError.PedidoAndRepresentanteMismatch; }
        }

        protected override PedidoError DuplicatedError { get { return PedidoError.Duplicated; } }

        protected override void SetProperties(IEnumerable<Pedido> pedidos)
        {
            base.SetProperties(pedidos);
            if (pedidos.Any())
            {
                Dictionary<Guid, Cliente> clientes = GetDependentModels(pedidos, x => x.Cliente);
                Dictionary<Guid, Vendedor> vendedores = GetDependentModels(pedidos, x => x.Vendedor);
                Dictionary<Guid, Fabrica> fabricas = GetDependentModels(pedidos, x => x.Fabrica);
                Dictionary<Guid, Transportadora> transportadoras = GetDependentModels(pedidos, x => x.Transportadora);

                foreach (Pedido pedido in pedidos)
                {
                    pedido.ClienteNome = clientes[pedido.Cliente.Id].RazaoSocial;
                    pedido.VendedorNome = vendedores[pedido.Vendedor.Id].Nome;
                    pedido.FabricaNome = fabricas[pedido.Fabrica.Id].Nome;
                    pedido.TransportadoraNome = transportadoras[pedido.Transportadora.Id].Nome;
                }
            }
        }

        protected override PedidoStatistics GetStatistics(int modelCount)
        {
            Guid? representanteId = GetRepresentanteId();

            int pecas = Convert.ToInt32(Session.CreateCriteria<Pedido>()
                .SetProjection(Projections.Sum<Pedido>(x => x.TotalPecas))
                .Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Representante.Id), representanteId))
                .UniqueResult());

            double valorTotal = Convert.ToDouble(Session.CreateCriteria<Pedido>()
                .SetProjection(Projections.Sum<Pedido>(x => x.TotalMercadoria))
                .Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Representante.Id), representanteId))
                .UniqueResult());

            Dictionary<string, double> vendedores = null;
            if (User.HasPermission(Area.Vendedores, Operacao.View))
            {
                vendedores = Session.CreateSQLQuery(@"
                    SELECT Nome, SUM(TotalLiquido) AS Total
                    FROM Vendedor
                    INNER JOIN Pedido ON Vendedor.UsuariosHolderId = Pedido.VendedorId
                    INNER JOIN AttributeHolder ON Vendedor.UsuariosHolderId = AttributeHolder.Id
                    WHERE RepresentanteId = :RepresentanteId
                    GROUP BY Nome")
                    .AddScalar("Nome", NHibernateUtil.String)
                    .AddScalar("Total", NHibernateUtil.Double)
                    .SetParameter("RepresentanteId", representanteId)
                    .List()
                    .Cast<object[]>()
                    .ToDictionary(x => (string) x[0], x => (double) x[1]);
            }

            Dictionary<string, double> fabricas = null;
            if (User.HasPermission(Area.Fabricas, Operacao.View))
            {
                fabricas = Session.CreateSQLQuery(@"
                    SELECT Nome, SUM(TotalLiquido) AS Total
                    FROM Fabrica
                    INNER JOIN Pedido ON Fabrica.UsuariosHolderId = Pedido.FabricaId
                    INNER JOIN AttributeHolder ON Fabrica.UsuariosHolderId = AttributeHolder.Id
                    WHERE RepresentanteId = :RepresentanteId
                    GROUP BY Nome")
                    .AddScalar("Nome", NHibernateUtil.String)
                    .AddScalar("Total", NHibernateUtil.Double)
                    .SetParameter("RepresentanteId", representanteId)
                    .List()
                    .Cast<object[]>()
                    .ToDictionary(x => (string) x[0], x => (double) x[1]);
            }

            return new PedidoStatistics(modelCount, pecas, valorTotal, 100, vendedores, fabricas);
        }

        private Dictionary<Guid, TModel> GetDependentModels<TModel>(IEnumerable<Pedido> pedidos, Func<Pedido, TModel> getDependentModel)
            where TModel : ModelBase
        {
            IEnumerable<Guid> ids = pedidos.Select(getDependentModel)
                .Where(model => model != null)
                .Select(model => model.Id)
                .Distinct();
            return Session.QueryOver<TModel>()
                .Where(x => x.Id.IsIn(ids.Cast<object>().ToArray()))
                .List()
                .ToDictionary(x => x.Id, x => x);
        }

        private IQueryOver<Pedido, Pedido> AddFiltersToQueryOver(IQueryOver<Pedido, Pedido> queryOver)
        {
            queryOver = AddFilterToQueryOver<Cliente>(queryOver, x => x.Cliente.Id);
            queryOver = AddFilterToQueryOver<Vendedor>(queryOver, x => x.Vendedor.Id);
            queryOver = AddFilterToQueryOver<Fabrica>(queryOver, x => x.Fabrica.Id);
            queryOver = AddFilterToQueryOver<Transportadora>(queryOver, x => x.Transportadora.Id);
            return queryOver;
        }

        private IQueryOver<Pedido, Pedido> AddFilterToQueryOver<TModel>(IQueryOver<Pedido, Pedido> queryOver, Expression<Func<Pedido, object>> property)
            where TModel : ModelBase
        {
            IEnumerable<TModel> filtro = User.Filtros.OfType<TModel>();
            if (filtro.Any())
            {
                queryOver = queryOver.And(Restrictions.In(Projections.Property(property), filtro.Select(x => x.Id).ToArray()));
            }
            return queryOver;
        }

        protected override void DoOnBeforeRemove(Pedido model)
        {
            model.ClearPosts();
        }

        public Dictionary<string, double> GetVendasForVendedor(Guid vendedorId)
        {
            if (Session.Connection.GetType().FullName == "System.Data.SQLite.SQLiteConnection") return null;

            IEnumerable<Vendedor> vendedorFiltro = User.Filtros.OfType<Vendedor>();
            if (vendedorFiltro.Any() && vendedorFiltro.All(x => x.Id != vendedorId))
            {
                throw new RestrictedAreaException();
            }

            if (Session.Get<Vendedor>(vendedorId).Representante.Id != GetRepresentante().Id)
            {
                ThrowRepositoryException(PedidoError.PedidoAndRepresentanteMismatch);
            }

            DateTime today = DateTime.Today;
            DateTime startFrom = today.AddYears(-1).AddMonths(1);
            Dictionary<string, double> values = new Dictionary<string, double>();
            ArrayList list = (ArrayList) Session.CreateSQLQuery(@"
                SELECT Month(Data) AS Mes, FLOOR(SUM(Quantidade * PrecoUnitario)) AS Total
                FROM Pedido
                INNER JOIN Produto ON Pedido.AttributeHolderId = Produto.PedidoId
                WHERE Pedido.VendedorId = :VendedorId AND
                    Data >= :From AND DATA <= :To AND
                    Cancelado = 0
                GROUP BY Month(Data)")
                .AddScalar("Mes", NHibernateUtil.Int32)
                .AddScalar("Total", NHibernateUtil.Double)
                .SetParameter("VendedorId", vendedorId)
                .SetParameter("From", startFrom)
                .SetParameter("To", today)
                .List();

            Dictionary<int, double> totais = list.ToArray().Cast<object[]>().ToDictionary(x => (int)x[0], x => (double)x[1]);
            for (DateTime date = startFrom; date.CompareTo(today) <= 0; date = date.AddMonths(1))
            {
                double total;
                if (!totais.TryGetValue(date.Month, out total))
                {
                    total = 0;
                }
                values.Add(String.Format("{0:MMM yy}", date), total);
            }
            return values;
        }

        public void AddPost(Post post)
        {
            using (ITransaction transaction = Session.BeginTransaction())
            {
                Session.Save(post);
                transaction.Commit();
            }
        }

        public IEnumerable<Pedido> GetModels(int? numero, IEnumerable<Guid> vendedorIds, string cliente, IEnumerable<Guid> fabricaIds, IEnumerable<Guid> transportadoraIds, DateTime? de, DateTime? ate, double? totalMin, double? totalMax, string observacao, bool? faturado, bool? cancelado, int? numeroProdutos, int? numeroPecas, string descricaoProduto, DateTime? criadoDe, DateTime? criadoAte, DateTime? modificadoDe, DateTime? modificadoAte, Dictionary<Attribute, string> attributeMap, ISearchExtension[] searchExtensions, out PedidoStatistics statistics, int page)
        {
            const string TableAlias = "pe";

            ICriteria criteria = CreateCriteria(attributeMap, TableAlias)
                .AddNumeroFilterForPedido(numero)
                .AddFabricaFilterForPedido(fabricaIds)
                .AddTransportadoraFilterForPedido(transportadoraIds)
                .AddDateFilterForPedido(de, ate, criadoDe, criadoAte, modificadoDe, modificadoAte)
                .AddFaturadoCanceladoFilterForPedido(faturado, cancelado)
                .AddObservacaoFilterForPedido(observacao);

            if (totalMin.HasValue || totalMax.HasValue)
            {
                DetachedCriteria detachedCriteria = DetachedCriteria.For<Produto>()
                    .SetProjection(Projections.SqlProjection("SUM(Quantidade * PrecoUnitario)", null, null))
                    .Add(Restrictions.EqProperty(TableAlias + ".Id", Projections.Property<Produto>(x => x.Pedido.Id)));
                if (totalMax.HasValue && totalMin.HasValue)
                {
                    criteria = criteria.Add(Restrictions.Between(Projections.SubQuery(detachedCriteria), totalMin.Value, totalMax));
                }
                else if (totalMin.HasValue)
                {
                    criteria = criteria.Add(Restrictions.Ge(Projections.SubQuery(detachedCriteria), totalMin.Value));
                }
                else
                {
                    criteria = criteria.Add(Restrictions.Le(Projections.SubQuery(detachedCriteria), totalMax.Value));
                }
            }

            if (numeroProdutos.HasValue)
            {
                DetachedCriteria detachedCriteria = DetachedCriteria.For<Produto>()
                    .SetProjection(Projections.SqlProjection("COUNT(*)", null, null))
                    .Add(Restrictions.EqProperty(TableAlias + ".Id", Projections.Property<Produto>(x => x.Pedido.Id)));
                criteria = criteria.Add(Restrictions.Eq(Projections.SubQuery(detachedCriteria), numeroProdutos.Value));
            }

            if (numeroPecas.HasValue)
            {
                DetachedCriteria detachedCriteria = DetachedCriteria.For<Produto>()
                    .SetProjection(Projections.SqlProjection("SUM(Quantidade)", null, null))
                    .Add(Restrictions.EqProperty(TableAlias + ".Id", Projections.Property<Produto>(x => x.Pedido.Id)));
                criteria = criteria.Add(Restrictions.Eq(Projections.SubQuery(detachedCriteria), numeroPecas.Value));
            }

            if (!String.IsNullOrWhiteSpace(descricaoProduto))
            {
                string descricaoProdutoLike = "%" + descricaoProduto.ToLower() + "%";
                criteria.CreateCriteria(TableAlias + ".Produtos", JoinType.InnerJoin)
                    .Add(Restrictions.InsensitiveLike(Projections.Property<Produto>(x => x.Descricao), descricaoProdutoLike));
            }

            criteria = criteria
                .AddClienteFilterForPedido(TableAlias, cliente)
                .AddUserFiltersForPedido(User);

            foreach (ISearchExtension searchExtension in searchExtensions)
            {
                criteria = searchExtension.Execute(criteria, TableAlias);
            }

            ICriteria criteriaNoVendedorFilter = (ICriteria)criteria.Clone();
            criteria = criteria.AddVendedorFilterForPedido(vendedorIds);

            double valorTotal = Convert.ToDouble(((ICriteria)criteria.Clone())
                .SetProjection(Projections.Sum<Pedido>(x => x.TotalMercadoria))
                .UniqueResult());

            double porcentagem;
            if (vendedorIds == null || !vendedorIds.Any())
            {
                porcentagem = 100;
            }
            else
            {
                double allVendedoresValorTotal = Convert.ToDouble(criteriaNoVendedorFilter
                    .SetProjection(Projections.Sum<Pedido>(x => x.TotalMercadoria))
                    .UniqueResult());

                porcentagem = 100 * valorTotal / allVendedoresValorTotal;
            }


            int pecas = 0;
            if (!numeroPecas.HasValue)
            {
                pecas = Convert.ToInt32(((ICriteria) criteria.Clone())
                    .SetProjection(Projections.Sum<Pedido>(x => x.TotalPecas))
                    .UniqueResult());
            }

            Dictionary<string, double> vendedores = User.HasPermission(Area.Vendedores, Operacao.View)
                ? GetVendas<Vendedor>(criteria, x => x.Vendedor.Id, "Vendedor.Id", x => x.Nome)
                : null;
            Dictionary<string, double> fabricas = User.HasPermission(Area.Fabricas, Operacao.View)
                ? GetVendas<Fabrica>(criteria, x => x.Fabrica.Id, "Fabrica.Id", x => x.Nome)
                : null;

            criteria = criteria.AddOrderForPedido();

            int modelCount;
            IEnumerable<Pedido> pedidos = FuturePaging(criteria, page, out modelCount);

            if (numeroPecas.HasValue)
            {
                pecas = numeroPecas.Value*modelCount;
            }

            SetProperties(pedidos);

            statistics = new PedidoStatistics(modelCount, pecas, valorTotal, porcentagem, vendedores, fabricas);
            
            return pedidos;
        }

        private Dictionary<string, double> GetVendas<T>(ICriteria criteria, Expression<Func<Pedido, object>> getModelId, string propertyName, Func<T, string> getName)
            where T : ModelBase
        {
            IEnumerable<T> vendedores = Session.CreateCriteria<T>()
                .Add(Subqueries.PropertyIn("Id", criteria.ToDetachedCriteria().SetProjection(Projections.Property(getModelId))))
                .List<T>();
            Dictionary<string, double> vendas = new Dictionary<string, double>();
            foreach (T model in vendedores)
            {
                double venda = Convert.ToDouble(((ICriteria)criteria.Clone())
                                                    .SetProjection(Projections.Sum<Pedido>(x => x.TotalMercadoria))
                                                    .Add(Restrictions.Eq(propertyName, model.Id))
                                                    .UniqueResult());
                vendas.Add(getName(model), venda);
            }
            return vendas;
        }

        public string[] FilterSynchronizedPedidos(string[] pedidos)
        {
            EnsureIsRepresentante();

            DetachedCriteria detachedCriteria = DetachedCriteria.For<Pedido>()
                .SetProjection(Projections.Property<Pedido>(x => x.Id))
                .Add(Restrictions.EqProperty("pj.Id", Projections.Property<Pedido>(x => x.Id)))
                .Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Representante.Id), GetRepresentanteId()));
            IEnumerable<string> synchronizedPedidos = Session.CreateCriteria<PedidoJolitex>("pj")
                .SetProjection(Projections.Property<PedidoJolitex>(x => x.Codigo))
                .Add(Restrictions.In(Projections.Property<PedidoJolitex>(x => x.Codigo), pedidos))
                .Add(Subqueries.Exists(detachedCriteria))
                .Future<string>()
                .Distinct();
            return pedidos.Except(synchronizedPedidos).ToArray();
        }

        public void AddModels(Pedido[] pedidos)
        {
            EnsureIsRepresentante();

            using (ITransaction transaction = Session.BeginTransaction())
            {
                IEnumerable<string> pedidosToSynchronize = 
                    FilterSynchronizedPedidos(pedidos.Select(x => x.PedidoJolitex.Codigo).ToArray());
                pedidos = pedidos
                    .Where(x => pedidosToSynchronize.Contains(x.PedidoJolitex.Codigo))
                    .ToArray();

                foreach (Pedido pedido in pedidos)
                {
                    OnBeforeSave(pedido);
                    Session.Save(pedido);
                }
                transaction.Commit();
            }
        }

        public IEnumerable<Pedido> GetModels(Guid? vendedorId, Guid? clienteId, Guid? fabricaId, Guid? transportadoraId, int count)
        {
            EnsureIsRepresentante();

            ICriteria criteria = Session.CreateCriteria<Pedido>()
                .Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Representante.Id), GetRepresentanteId()));

            if (vendedorId.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Vendedor.Id), vendedorId.Value));
            }
            if (clienteId.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Cliente.Id), clienteId.Value));
            }
            if (fabricaId.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Fabrica.Id), fabricaId.Value));
            }
            if (transportadoraId.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Transportadora.Id), transportadoraId.Value));
            }

            criteria = criteria.AddUserFiltersForPedido(User);

            criteria = criteria.AddOrderForPedido();

            int modelCount;
            m_PageSize = count;
            IEnumerable<Pedido> pedidos = FuturePaging(criteria, 1, out modelCount);
            m_PageSize = null;
            return pedidos;
        }

        public IEnumerable<CompareYear> GetCompare(CompareTotal compareTotal, IEnumerable<Guid> fabricaIds, IEnumerable<Guid> vendedorIds, string cliente, IEnumerable<Guid> transportadoraIds, bool allJolitex)
        {
            string totalColumn = null;
            switch (compareTotal)
            {
                case CompareTotal.Count:
                    totalColumn = "COUNT(*)";
                    break;
                case CompareTotal.TotalLiquido:
                    totalColumn = "SUM(TotalLiquido)";
                    break;
                case CompareTotal.TotalMercadoria:
                    totalColumn = "SUM(TotalMercadoria)";
                    break;
                case CompareTotal.TotalPecas:
                    totalColumn = "SUM(TotalPecas)";
                    break;
                default:
                    ThrowRepositoryException(PedidoError.InvalidCompareTotal);
                    break;
            }
            string fabricaWhere = String.Empty;
            string fabricaJolitexJoin = String.Empty;
            if (allJolitex)
            {
                fabricaJolitexJoin = "INNER JOIN FabricaJolitex ON FabricaJolitex.Id = FabricaId";
                fabricaWhere = "AND IsJolitex = 1";
            }
            else if (fabricaIds.Any())
            {
                fabricaWhere = String.Format("AND FabricaId IN ('{0}')", String.Join("','", fabricaIds));
            }
            string vendedorWhere = String.Empty;
            if (vendedorIds.Any())
            {
                vendedorWhere = String.Format("AND VendedorId IN ('{0}')", String.Join("','", vendedorIds));
            }
            string transportadoraWhere = String.Empty;
            if (transportadoraIds.Any())
            {
                transportadoraWhere = String.Format("AND TransportadoraId IN ('{0}')", String.Join("','", transportadoraIds));
            }
            string clienteWhere = String.Empty;
            string clienteJoin = String.Empty;
            if (!String.IsNullOrWhiteSpace(cliente))
            {
                clienteJoin = "INNER JOIN Cliente ON ClienteId = Cliente.UsuariosHolderId";
                clienteWhere = String.Format("AND (RazaoSocial LIKE '%{0}%' OR NomeFantasia LIKE '%{0}%')", cliente);
            }
            ArrayList list = (ArrayList) Session.CreateSQLQuery(String.Format(@"
                SELECT {0} AS Total, MONTH(Data) AS Month, YEAR(Data) AS Year
                FROM Pedido
                INNER JOIN AttributeHolder ON Id = AttributeHolderId
                {1}
                {2}
                WHERE RepresentanteId = :RepresentanteId AND Cancelado = 0
                {3}
                {4}
                {5}
                {6}
                GROUP BY MONTH(Data), YEAR(Data)
                ORDER BY YEAR(Data), MONTH(Data)", totalColumn, fabricaJolitexJoin, clienteJoin, fabricaWhere, vendedorWhere, clienteWhere, transportadoraWhere))
                .AddScalar("Total", NHibernateUtil.Double)
                .AddScalar("Month", NHibernateUtil.Int32)
                .AddScalar("Year", NHibernateUtil.Int32)
                .SetParameter("RepresentanteId", GetRepresentanteId())
                .List();
            List<CompareYear> result = new List<CompareYear>();
            CompareYear compareYear = null;
            foreach (object[] item in list)
            {
                double total = (double) item[0];
                int month = (int) item[1];
                int year = (int) item[2];
                if (compareYear == null || compareYear.Year != year)
                {
                    compareYear = new CompareYear(year);
                    result.Add(compareYear);
                }
                compareYear.AddMonth(month, total);
            }
            return result;
        }

        public override int PageSize
        {
            get { return m_PageSize.HasValue ? m_PageSize.Value : base.PageSize; }
        }

        protected override void OnBeforeSave(Pedido model)
        {
            base.OnBeforeSave(model);
            model.SetCalculatedValues(m_ImpostoProvider);
            model.Created = DateTime.Now;
            model.Modified = model.Created;
        }

        protected override void OnBeforeUpdate(Pedido model)
        {
            base.OnBeforeUpdate(model);
            model.SetCalculatedValues(m_ImpostoProvider);
            model.Modified = DateTime.Now;
        }
    }

    public enum PedidoError
    {
        PedidoAndRepresentanteMismatch,
        Duplicated,
        InvalidCompareTotal
    }

    public static class PedidoRepositoryHelper
    {
        public static ICriteria AddNumeroFilterForPedido(this ICriteria criteria, int? numero)
        {
            if (numero.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Numero), numero.Value));
            }
            return criteria;
        }

        public static ICriteria AddFabricaFilterForPedido(this ICriteria criteria, IEnumerable<Guid> fabricaIds)
        {
            if (fabricaIds != null && fabricaIds.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Fabrica.Id), fabricaIds.ToArray()));
            }
            return criteria;
        }

        public static ICriteria AddTransportadoraFilterForPedido(this ICriteria criteria, IEnumerable<Guid> transportadoraIds)
        {
            if (transportadoraIds != null && transportadoraIds.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Transportadora.Id), transportadoraIds.ToArray()));
            }
            return criteria;
        }

        public static ICriteria AddDateFilterForPedido(this ICriteria criteria, DateTime? de, DateTime? ate, DateTime? criadoDe = null, DateTime? criadoAte = null, DateTime? modificadoDe = null, DateTime? modificadoAte = null)
        {
            if (de.HasValue)
            {
                criteria = criteria.Add(Restrictions.Ge(Projections.Property<Pedido>(x => x.Data), de.Value));
            }

            if (ate.HasValue)
            {
                criteria = criteria.Add(Restrictions.Le(Projections.Property<Pedido>(x => x.Data), ate.Value));
            }

            if (criadoDe.HasValue)
            {
                criteria = criteria.Add(Restrictions.Ge(Projections.Property<Pedido>(x => x.Created), criadoDe.Value));
            }

            if (criadoAte.HasValue)
            {
                criteria = criteria.Add(Restrictions.Le(Projections.Property<Pedido>(x => x.Created), criadoAte.Value));
            }

            if (modificadoDe.HasValue)
            {
                criteria = criteria.Add(Restrictions.Ge(Projections.Property<Pedido>(x => x.Modified), modificadoDe.Value));
            }

            if (modificadoAte.HasValue)
            {
                criteria = criteria.Add(Restrictions.Le(Projections.Property<Pedido>(x => x.Modified), modificadoAte.Value));
            }

            return criteria;
        }

        public static ICriteria AddFaturadoCanceladoFilterForPedido(this ICriteria criteria, bool? faturado, bool? cancelado)
        {
            if (faturado.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Faturado), faturado.Value));
            }

            if (cancelado.HasValue)
            {
                criteria = criteria.Add(Restrictions.Eq(Projections.Property<Pedido>(x => x.Cancelado), cancelado.Value));
            }
            return criteria;
        }

        public static ICriteria AddObservacaoFilterForPedido(this ICriteria criteria, string observacao)
        {
            if (!String.IsNullOrWhiteSpace(observacao))
            {
                criteria = criteria.Add(Restrictions.Like(Projections.Property<Pedido>(x => x.Observacao), observacao));
            }
            return criteria;
        }

        public static ICriteria AddClienteFilterForPedido(this ICriteria criteria, string tableAlias, string cliente)
        {
            if (!String.IsNullOrEmpty(cliente))
            {
                string clienteLike = "%" + cliente.ToLower() + "%";
                criteria.CreateCriteria(tableAlias + ".Cliente", JoinType.InnerJoin)
                    .Add(Restrictions.Or(
                        Restrictions.InsensitiveLike(Projections.Property<Cliente>(x => x.NomeFantasia), clienteLike),
                        Restrictions.InsensitiveLike(Projections.Property<Cliente>(x => x.RazaoSocial), clienteLike)));
            }
            return criteria;
        }

        public static ICriteria AddUserFiltersForPedido(this ICriteria criteria, User user)
        {
            IEnumerable<Cliente> clienteFiltro = user.Filtros.OfType<Cliente>();
            if (clienteFiltro.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Cliente.Id), clienteFiltro.Select(y => y.Id).ToArray()));
            }

            IEnumerable<Fabrica> fabricaFiltro = user.Filtros.OfType<Fabrica>();
            if (fabricaFiltro.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Fabrica.Id), fabricaFiltro.Select(y => y.Id).ToArray()));
            }

            IEnumerable<Transportadora> transportadoraFiltro = user.Filtros.OfType<Transportadora>();
            if (transportadoraFiltro.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Transportadora.Id), transportadoraFiltro.Select(y => y.Id).ToArray()));
            }

            IEnumerable<Vendedor> vendedorFiltro = user.Filtros.OfType<Vendedor>();
            if (vendedorFiltro.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Vendedor.Id), vendedorFiltro.Select(y => y.Id).ToArray()));
            }
            return criteria;
        }

        public static ICriteria AddVendedorFilterForPedido(this ICriteria criteria, IEnumerable<Guid> vendedorIds)
        {
            if (vendedorIds != null && vendedorIds.Any())
            {
                criteria = criteria.Add(Restrictions.In(Projections.Property<Pedido>(x => x.Vendedor.Id), vendedorIds.ToArray()));
            }
            return criteria;
        }

        public static ICriteria AddOrderForPedido(this ICriteria criteria)
        {
            return criteria
                .AddOrder(Order.Desc(Projections.Property<Pedido>(x => x.Data)))
                .AddOrder(Order.Desc(Projections.Property<Pedido>(x => x.Numero)));
        }
    }
}
